/**
 * Alipay.com Inc. Copyright (c) 2004-2018 All Rights Reserved.
 */
package com.yeyks.controller;

import com.alibaba.fastjson.JSONObject;
import com.yeyks.apppay.service.AlipayService;
import com.yeyks.apppay.service.WXAppPayService;
import com.yeyks.base.Arguments;
import com.yeyks.constant.ConfigKey;
import com.yeyks.dal.dao.*;
import com.yeyks.dal.domain.*;
import com.yeyks.exception.PermissionInvalidException;
import com.yeyks.exception.ServiceException;
import com.yeyks.exception.TokenInvalidException;
import com.yeyks.model.Ajax.AjaxResponseBody;
import com.yeyks.model.OrderStatus;
import com.yeyks.model.PreOrderInfo;
import com.yeyks.model.PreOrderType;
import com.yeyks.model.em.AccountType;
import com.yeyks.model.em.ResponseStatus;
import com.yeyks.model.enums.PayTypeEnum;
import com.yeyks.model.enums.RefundTypeEnum;
import com.yeyks.newwebsocket.WebSocket;
import com.yeyks.service.*;
import com.yeyks.util.DateUtils;
import com.yeyks.util.Utility;
import com.yeyks.wxpay.impl.MyConfig;
import com.yeyks.wxpay.impl.WXPayUtility;
import com.github.binarywang.wxpay.bean.entpay.EntPayResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.yeyks.exception.PermissionInvalidException;
import com.yeyks.exception.TokenInvalidException;
import com.yeyks.model.Ajax.AjaxResponseBody;
import com.yeyks.model.OrderStatus;
import com.yeyks.model.PreOrderInfo;
import com.yeyks.model.PreOrderType;
import com.yeyks.model.em.AccountType;
import com.yeyks.model.em.ResponseStatus;
import com.yeyks.service.*;
import com.yeyks.util.Utility;
import com.yeyks.wxpay.impl.MyConfig;
import com.yeyks.wxpay.impl.WXPayUtility;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.InvalidParameterException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author Beacon
 * @version $Id: OrderController.java, v 0.1 2018年08月09日 20:08 Beacon Exp $
 */
@RestController
@Slf4j
@RequestMapping(value = "/ajax/order")
public class OrderController {

    @Autowired
    private AccountInfoMapper accountInfoMapper;

    @Autowired
    private AccountService accountService;

    @Autowired
    private RoomInfoMapper roomInfoMapper;

    @Autowired
    private HotelInfoMapper hotelInfoMapper;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderService orderService;

    @Autowired
    private PayService payService;

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private CouponInfoMapper couponInfoMapper;

    @Autowired
    private UserCouponInfoMapper userCouponInfoMapper;

    @Autowired
    private MessageService messageService;

    @Autowired
    private AlipayService alipayService;

    @Autowired
    private WXAppPayService wxAppPayService;

    @Autowired
    private UserCouponService userCouponService;

    @Autowired
    private SmsService smsService;

    @Autowired
    private HotelService hotelService;

    @Autowired
    private WebSocket webSocket;

    @Autowired
    private ConfigService configService;

    private static final Integer mockTotalFee = null;

    @Value("${wx.pay.oldNotifyUrl}")
    private String oldNotifyUrl;

    /**
     * 订单退款
     *
     * @param request
     * @param response
     * @param token
     * @param orderNo
     * @param reason
     * @param refund
     * @param accept
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/refundOrder", method = RequestMethod.POST)
    public ResponseEntity<?> refundOrder(
            HttpServletRequest request, HttpServletResponse response,
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "orderNo", defaultValue = "", required = true) String orderNo,
            @RequestParam(name = "reason", defaultValue = "", required = true) String reason,
            @RequestParam(name = "refund", defaultValue = "", required = true) Integer refund,
            @RequestParam(name = "accept", defaultValue = "true", required = true) Boolean accept
    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {
            OrderInfo orderInfo = orderInfoMapper.selectByOrderNo(orderNo);
            if (orderInfo == null) {
                throw new InvalidParameterException("order information is null.");
            }

            AccountInfo operatorInfo = accountService.token2AccountInfo(token);
            if (operatorInfo.getType() == AccountType.User) {
                if (orderInfo.getUserId().intValue() != operatorInfo.getId().intValue()) {
                    throw new PermissionInvalidException();
                }

                orderInfo.setStatus(OrderStatus.preRefund);
                try {
                    orderInfo.setExtraInfo(Utility.setExtraInfo(orderInfo.getExtraInfo(), "refund_fee", refund));
                } catch (Throwable e) {
                    e.printStackTrace();
                }

                try {
                    orderInfo.setExtraInfo(Utility.setExtraInfo(orderInfo.getExtraInfo(), "refund_reason", reason));
                } catch (Throwable e) {
                    e.printStackTrace();
                }

                int rec = orderInfoMapper.updateByPrimaryKey(orderInfo);
                if (rec == 0) {
                    throw new Exception("updated 0 row.");
                }
            } else if (operatorInfo.getType() == AccountType.HotelManager) {

                if (accept) {

                    Integer hotelId = Integer.parseInt(Utility.getExtraInfo(orderInfo.getExtraInfo(), "snapshot_hotel_id"));
                    if (hotelId == null || hotelId == 0) {
                        throw new Exception("no hotel id");
                    }
                    HotelInfo hotelInfo = hotelInfoMapper.selectByPrimaryKey(hotelId);
                    if (hotelInfo == null) {
                        throw new Exception("no hotel information");
                    } else if (hotelInfo.getOperatorId().intValue() != operatorInfo.getId().intValue()) {
                        throw new PermissionInvalidException();
                    }

                    Integer totalFee = Integer.parseInt(Utility.getExtraInfo(orderInfo.getExtraInfo(), "snapshot_totalfee"));

                    Integer refundFee = null;
                    try {
                        refundFee = Integer.parseInt(Utility.getExtraInfo(orderInfo.getExtraInfo(), "refund_fee"));
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                    if (refundFee == null || refundFee == 0 || refundFee > totalFee) {
                        refundFee = totalFee;
                    }

                    if (mockTotalFee != null) {
                        totalFee = mockTotalFee;
                        refundFee = mockTotalFee;
                    }

                    String refundNo = new Date().getTime() + Utility.randomString(7);
                    WxPayRefundRequest refundRequest = WxPayRefundRequest.newBuilder()
                            .notifyUrl("http://kuaisu.chengguokj.com/ajax/wxpay/refundCallback")
                            .outTradeNo(orderNo)
                            .outRefundNo(refundNo)
                            .totalFee(totalFee)
                            .refundFee(refundFee)
                            .build();
                    wxPayService.refund(refundRequest);

                    orderInfo.setSnapshotRefundFee(refundFee);
                    int rec = orderInfoMapper.updateByPrimaryKey(orderInfo);
                    if (rec == 0) {
                        throw new Exception("updated 0 row.");
                    }
                } else {
                    orderInfo.setStatus(OrderStatus.preCheckIn);
                    int rec = orderInfoMapper.updateByPrimaryKey(orderInfo);
                    if (rec == 0) {
                        throw new Exception("updated 0 row.");
                    }

                    messageService.rejectRefund(orderInfo.getUserId());
                }
            }

            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(e.getMessage());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(e.getMessage());
        }
        return ResponseEntity.ok(responseBody);
    }

    /**
     * 订单退款
     *
     * @param token
     * @param orderNo
     * @param accept
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/refundOrderV2", method = RequestMethod.POST)
    public ResponseEntity<?> refundOrderV2(
            @RequestParam(name = "token") String token,
            @RequestParam(name = "orderNo") String orderNo,
            @RequestParam(name = "accept", defaultValue = "true") Boolean accept,
            @RequestParam(name = "type", defaultValue = "user") String type) {
        AccountInfo operatorInfo = accountService.token2AccountInfoV2(token);
        if (operatorInfo.getType() != AccountType.HotelManager
                && operatorInfo.getType() != AccountType.Administrator) {
            throw new ServiceException("当前用户无权操作");
        }

        OrderInfo orderInfo = orderInfoMapper.selectByOrderNo(orderNo);
        if (orderInfo == null) {
            throw new ServiceException("通过该订单号查询不到该订单");
        }
        //酒店方超过三分钟无法接单不能点击
        if (type.equals(RefundTypeEnum.HOTEL.desc())
                && operatorInfo.getType().equals(AccountType.HotelManager)
                && orderInfo.getStatus().equals(OrderStatus.preCheckIn)) {
            Date date = DateUtils.dateAfterMinute(orderInfo.getGmtCreate(), getMinutes());
            if (DateUtils.dateCompareDate(new Date(), date)) {
                throw new ServiceException("超过三分钟，不能执行无法接单操作");
            }
        }
        if (accept) {
            Integer hotelId = Integer.parseInt(Utility.getExtraInfo(orderInfo.getExtraInfo(), "snapshot_hotel_id"));
            if (hotelId == 0) {
                throw new ServiceException("订单中没有该酒店id");
            }
            HotelInfo hotelInfo = hotelInfoMapper.selectByPrimaryKey(hotelId);
            if (hotelInfo == null) {
                throw new ServiceException("没有该酒店信息");
            }
            //获取退款金额，商家点击退款全额退款，用户点击退款部分退
            Integer refundAmount = 0;
            if (type.equals(RefundTypeEnum.USER.desc())) {
                refundAmount = getRefundAmount(orderInfo);
            }
            if (type.equals(RefundTypeEnum.HOTEL.desc())) {
                refundAmount = orderInfo.getSnapshotTotalFee();
            }
            if (refundAmount == 0) {
                //退款成功，如果使用券，将券的状态修改为未使用
                updateCouponUse(orderInfo);
                //增加房间数
                updateRoomCount(orderInfo);
                orderInfo.setStatus(OrderStatus.postRefund);
            } else {
                //支付宝退款(元)
                if (orderInfo.getPaymentMethod().equals(PayTypeEnum.ALIPAY.value())) {
                    Boolean flag = alipayService.tradeRefund(orderNo, refundAmount);
                    if (flag) {
                        //退款成功，如果使用券，将券的状态修改为未使用
                        updateCouponUse(orderInfo);
                        //增加房间数
                        updateRoomCount(orderInfo);
                        orderInfo.setStatus(OrderStatus.postRefund);
                    } else {
                        throw new ServiceException("支付宝退款失败");
                    }
                }
                //微信退款（分）
                if (orderInfo.getPaymentMethod().equals(PayTypeEnum.WXPAY.value())) {
                    Boolean flag = wxAppPayService.refund(refundAmount, orderInfo.getSnapshotTotalFee(), orderNo);
                    if (flag) {
                        //退款成功，如果使用券，将券的状态修改为未使用
                        updateCouponUse(orderInfo);
                        //增加房间数
                        updateRoomCount(orderInfo);
                        orderInfo.setStatus(OrderStatus.postRefund);
                    } else {
                        throw new ServiceException("微信退款失败");
                    }
                }
                //微信JSAPI退款（分）
                if (orderInfo.getPaymentMethod().equals(PayTypeEnum.GZH.value())
                        || orderInfo.getPaymentMethod().equals(PayTypeEnum.XCX.value())
                        || orderInfo.getPaymentMethod().equals(PayTypeEnum.JSAPI.value())) {
                    String refundNo = orderNo + 888;
                    WxPayRefundRequest refundRequest = WxPayRefundRequest.newBuilder()
                            .outTradeNo(orderNo)
                            .outRefundNo(refundNo)
                            .totalFee(orderInfo.getSnapshotTotalFee())
                            .refundFee(refundAmount)
                            .build();
                    Boolean flag = false;
                    try {
                        WxPayRefundResult refund = wxPayService.refund(refundRequest);
                        if (refund != null && refund.getReturnCode().equals("SUCCESS")
                                && refund.getResultCode().equals("SUCCESS")) {
                            log.info("退款参数{}", refund.toString());
                            flag = true;
                        }
                    } catch (WxPayException e) {
                        log.info("微信JSAPI退款失败" + e.toString());
                    }
                    if (flag) {
                        //退款成功，如果使用券，将券的状态修改为未使用
                        updateCouponUse(orderInfo);
                        //增加房间数
                        updateRoomCount(orderInfo);
                        orderInfo.setStatus(OrderStatus.postRefund);
                    } else {
                        throw new ServiceException("微信JSAPI退款失败");
                    }
                }
            }
            orderInfo.setSnapshotRefundFee(refundAmount);
            int rec = orderInfoMapper.updateByPrimaryKey(orderInfo);
            if (rec != 1) {
                throw new ServiceException("更新订单状态失败");
            }
            //发送相关短信
            refundSms(orderInfo, type, hotelInfo);
        } else {
            orderInfo.setStatus(OrderStatus.preCheckIn);
            int rec = orderInfoMapper.updateByPrimaryKey(orderInfo);
            if (rec == 0) {
                throw new ServiceException("拒绝退款修改状态失败");
            }
            messageService.rejectRefund(orderInfo.getUserId());
        }
        return ResponseEntity.ok(AjaxResponseBody.success());
    }

    private Integer getMinutes() {
        try {
            String value = configService.getValue(ConfigKey.ORDER_NO_ACCEPT);
            JSONObject jsonObject = JSONObject.parseObject(value);
            return (Integer) jsonObject.get("noAccept");
        } catch (Exception e) {
            log.error("获取无法接单分钟数失败{}", e.getLocalizedMessage());
            return 3;
        }
    }

    private Integer getRefundAmount(OrderInfo orderInfo) {
        try {
            String extraInfo = orderInfo.getExtraInfo();
            JSONObject jsonObject = JSONObject.parseObject(extraInfo);
            return (Integer) jsonObject.get("refund_fee");

        } catch (Exception e) {
            log.info("订单退款中获取订单退款金额失败{}", e.getLocalizedMessage());
            return orderService.getRefundAmount(orderInfo);
        }
    }

    private void refundSms(OrderInfo orderInfo, String type, HotelInfo hotelInfo) {
        RoomInfo roomInfo = roomInfoMapper.selectByPrimaryKey(orderInfo.getRoomId());
        //酒店退款给用户发送短信
        if (type.equals(RefundTypeEnum.HOTEL.desc())) {
            String phone = orderService.getPhoneByOrderInfo(orderInfo);
//            String daytime = DateUtils.format(DateUtils.afterHoursToDate(orderInfo.getCheckIn(), 12L));
            String daytime = DateUtils.toYYMMddStr(orderInfo.getCheckIn());
            String hotelname = hotelInfo.getName();
            String roomtype = roomInfo == null ? null : roomInfo.getTitle();
            smsService.sendSmsForHotelRefund(phone, daytime, hotelname, roomtype);
        }
    }

    private String getPhoneByOrderInfo(OrderInfo orderInfo) {
        String phone;
        try {
            JSONObject jsonObject = JSONObject.parseObject(orderInfo.getVitalPersonInfo());
            phone = (String) jsonObject.get("phone");
            return phone;
        } catch (Exception e) {
            log.info("酒店退款给用户发短信，用户手机号不存在,orderNo = {}", orderInfo.getOrderNo());
            return null;
        }
    }

    private void updateRoomCount(OrderInfo orderInfo) {
        try {
            JSONObject jsonObject = JSONObject.parseObject(orderInfo.getExtraInfo());
            Integer count = (Integer) jsonObject.get("snapshot_count");
            RoomInfo roomInfo = roomInfoMapper.selectByPrimaryKey(orderInfo.getRoomId());
            //获取预定的房间个数
            roomInfo.setAvailable(roomInfo.getAvailable() + count);
            roomInfoMapper.updateIfNotNullByPrimaryKey(roomInfo);
        } catch (Exception e) {
            log.info("退款成功时修改房间状态失败{}", orderInfo.toString());
        }
    }

    private void updateCouponUse(OrderInfo orderInfo) {
        try {
            JSONObject jsonObject = JSONObject.parseObject(orderInfo.getExtraInfo());
            Integer couponId = (Integer) jsonObject.get("couponId");
            if (couponId != 0) {
                userCouponService.updateCouponUseById(couponId, false);
            }
        } catch (Exception e) {
            log.info("退款成功时修改券为未使用失败{}", orderInfo.toString());
        }
    }

    @ApiIgnore
    @RequestMapping(value = "/preOrderGetPrice", method = RequestMethod.POST)
    public ResponseEntity<?> preOrderGetPrice(
            HttpServletRequest request, HttpServletResponse response,
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "checkInDate", defaultValue = "", required = true) String checkInDate,
            @RequestParam(name = "checkOutDate", defaultValue = "", required = true) String checkOutDate,
            @RequestParam(name = "subject", defaultValue = "", required = true) String subject,
            @RequestParam(name = "body", defaultValue = "", required = true) String body,
            @RequestParam(name = "code", defaultValue = "", required = true) String code,
            @RequestParam(name = "count", defaultValue = "", required = true) Integer count,
            @RequestParam(name = "remark", defaultValue = "", required = true) String remark,
            @RequestParam(name = "couponId", defaultValue = "", required = true) Integer couponId,
            OrderInfo param
    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {

            RoomInfo roomInfo = roomInfoMapper.selectByPrimaryKey(param.getRoomId());
            if (roomInfo == null) {
                throw new Exception("Cannot find the room information.");
            }

            HotelInfo hotelInfo = hotelInfoMapper.selectByPrimaryKey(roomInfo.getHotelId());
            if (hotelInfo == null) {
                throw new Exception("Cannot find the hotel information.");
            }

            if (couponId != null && couponId != 0) {
                CouponInfo couponInfo = couponInfoMapper.selectByPrimaryKey(couponId);
                List<UserCouponInfo> userCouponInfos = userCouponInfoMapper.selectUnusedByCouponId(couponId);
                if (userCouponInfos != null && userCouponInfos.size() != 0 &&
                        couponInfo.getValidHotels().indexOf("|" + hotelInfo.getId() + "|") != -1) {
                    Date now = new Date();
                    if (now.getTime() >= couponInfo.getValidStart().getTime() &&
                            now.getTime() <= couponInfo.getValidEnd().getTime()
                            ) {

                    }
                }
            }

            final long daytime = (24 * 60 * 60 * 1000);
            long diff = param.getCheckOut().getTime() - param.getCheckIn().getTime();
            long days = (diff + daytime - 1) / daytime;

            Integer totalFee = (int) days * roomInfo.getCurPrice();
            if (count != null && count != 0) {
                totalFee *= count;
            }

            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(e.getMessage());
        }

        return ResponseEntity.ok(responseBody);
    }

    /**
     * 生成酒店订单
     *
     * @param request
     * @param response
     * @param token
     * @param checkInDate
     * @param checkOutDate
     * @param subject
     * @param body
     * @param code
     * @param count
     * @param remark
     * @param coupon
     * @param sourceType
     * @param param
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/preOrder", method = RequestMethod.POST)
    public ResponseEntity<?> preOrder(
            HttpServletRequest request, HttpServletResponse response,
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "checkInDate", defaultValue = "", required = true) String checkInDate,
            @RequestParam(name = "checkOutDate", defaultValue = "", required = true) String checkOutDate,
            @RequestParam(name = "subject", defaultValue = "", required = true) String subject,
            @RequestParam(name = "body", defaultValue = "", required = true) String body,
            @RequestParam(name = "code", defaultValue = "", required = true) String code,
            @RequestParam(name = "count", defaultValue = "", required = true) Integer count,
            @RequestParam(name = "remark", defaultValue = "", required = true) String remark,
            @RequestParam(name = "coupon", defaultValue = "", required = true) Integer coupon,
            @RequestParam(name = "sourceType", defaultValue = "wx", required = true) String sourceType,
            OrderInfo param
    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {
            int orderType = PreOrderType.parse(sourceType);
            boolean usesConfig1 = orderType == PreOrderType.wx || orderType == PreOrderType.h5;

            AccountInfo accountInfo = accountService.token2AccountInfo(token);
            if (accountInfo.getType() != AccountType.User) {
                throw new PermissionInvalidException();
            }

            if (subject == null || subject.isEmpty() ||
                    body == null || body.isEmpty()) {
                throw new InvalidParameterException("subject or body is invalid");
            }

            param.setUserId(accountInfo.getId());
            param.setGmtCreate(new Date());
            param.setGmtModified(new Date());
            if (param.getPaymentMethod() == null) {
                param.setPaymentMethod(0);
            }

            param.setStatus(OrderStatus.prePay);

            RoomInfo roomInfo = roomInfoMapper.selectByPrimaryKey(param.getRoomId());
            if (roomInfo == null) {
                throw new Exception("Cannot find the room information.");
            }

            HotelInfo hotelInfo = hotelInfoMapper.selectByPrimaryKey(roomInfo.getHotelId());
            if (hotelInfo == null) {
                throw new Exception("Cannot find the hotel information.");
            }
            param.setSnapshotHotelName(hotelInfo.getName());
            param.setSnapshotRoomName(roomInfo.getTitle());
            param.setSnapshotRoomPrice(roomInfo.getCurPrice());

            JSONObject hotelPrincipalObj = Utility.toJsonObject(hotelInfo.getPrincipalInfo());


            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            try {
                if (param.getCheckIn() == null) {
                    param.setCheckIn(sdf.parse(checkInDate));
                }
            } catch (Throwable e) {
                e.printStackTrace();

            }
            try {
                if (param.getCheckOut() == null) {
                    param.setCheckOut(sdf.parse(checkOutDate));
                }
            } catch (Throwable e) {
                e.printStackTrace();

            }

            final long daytime = (24 * 60 * 60 * 1000);
            long diff = param.getCheckOut().getTime() - param.getCheckIn().getTime();
            long days = (diff + daytime - 1) / daytime;

            String openId = null;
            if (orderType != PreOrderType.h5) {
                if (code != null && !code.isEmpty()) {
                    try {
                        MyConfig config = new MyConfig();
                        Utility.switchConfig(usesConfig1, null, config);
                        JSONObject jsonObject = payService.wxParseCode(usesConfig1, code, config);
                        String refreshToken = payService.wxCodeObject2RefreshToken(jsonObject);
                        if (refreshToken != null && !refreshToken.isEmpty()) {
                            String extraInfo = WXPayUtility.setRefreshTokenToExtraInfo(accountInfo.getExtraInfo(), refreshToken);
                            accountInfoMapper.updateExtraInfoById(accountInfo.getId(), extraInfo);
                        }

                        openId = payService.wxObject2OpenId(jsonObject);
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }

                // 没有拿到openId，尝试从缓存中读取
                if (openId == null || openId.isEmpty()) {
                    openId = WXPayUtility.getOpenIdFromExtraInfo(accountInfo.getExtraInfo());
                }

				/*
				// 去除OpenId判断
                if (openId == null || openId.isEmpty()) {
                    throw new Exception("openid error");
                }
				*/
            }

            Integer totalFee = (int) days * roomInfo.getCurPrice();
            if (count != null && count != 0) {
                totalFee *= count;
            }

            ////////////////////////////////////////////////////////////////////
            // 优惠券使用逻辑
            if (coupon != null) {
                List<Map> couponInfos = couponInfoMapper.selectByUser(accountInfo.getId(), false, true, coupon);
                if (couponInfos == null && couponInfos.size() == 0) {
                    responseBody.setCode(ResponseStatus.RS_COUPON_USE_ERROR.getCode());
                    responseBody.setMsg(ResponseStatus.RS_COUPON_USE_ERROR.getMsg());

                    return ResponseEntity.ok(responseBody);
                }

                Map couponInfo = couponInfos.get(0);
                Integer validFee = 0, price = 0;
                try {
                    validFee = Integer.parseInt(couponInfo.get("valid_fee").toString());
                } catch (Throwable e) {
                    e.printStackTrace();
                }
                try {
                    price = Integer.parseInt(couponInfo.get("price").toString());
                } catch (Throwable e) {
                    e.printStackTrace();
                }
                /*
                String validHotels = null;
                try {
                    validHotels = couponInfo.get("valid_hotels").toString();
                } catch (Throwable e) {

                }

                Boolean validHotel = true;
                if (!StringUtils.isBlank(validHotels)) {
                    List<Integer> ids = Utility.str2intList(validHotels, "|");
                    if (ids != null && ids.size() != 0) {
                        validHotel = false;
                        for (Integer id : ids) {
                            if (id.intValue() == hotelInfo.getId().intValue()) {
                                validHotel = true;
                                break;
                            }
                        }
                    }
                }

                if (!validHotel) {
                    responseBody.setCode(ResponseStatus.RS_COUPON_USE_NOTSUPPORT.getCode());
                    responseBody.setMsg(ResponseStatus.RS_COUPON_USE_NOTSUPPORT.getMsg());

                    return ResponseEntity.ok(responseBody);
                }
                */
                if (validFee != 0 && validFee > totalFee) {
                    responseBody.setCode(ResponseStatus.RS_COUPON_USE_VALIDFEE.getCode());
                    responseBody.setMsg(ResponseStatus.RS_COUPON_USE_VALIDFEE.getMsg());

                    return ResponseEntity.ok(responseBody);
                }

                if (price > totalFee) {
                    totalFee = 1;
                } else {
                    totalFee -= price;
                }

                /////////////////////////////////
                int rec = userCouponInfoMapper.updateUsed(coupon);
                if (rec == 0) {
                    throw new Exception("updated 0 row.");
                }
            }
            ////////////////////////////////////////////////////////////////////

            JSONObject orderExtraObject = Utility.toJsonObject(param.getExtraInfo());
            orderExtraObject.put("snapshot_hotel_address", hotelInfo.getAddress());
            orderExtraObject.put("snapshot_hotel_id", hotelInfo.getId());
            orderExtraObject.put("snapshot_hotel_phone", hotelPrincipalObj.getString("phone"));
            orderExtraObject.put("snapshot_pcinfo", accountService.getPrecentage());
            orderExtraObject.put("snapshot_totalfee", totalFee);
            orderExtraObject.put("snapshot_days", days);
            orderExtraObject.put("snapshot_count", count);
            orderExtraObject.put("remark", remark);
            param.setSnapshotTotalFee(totalFee);
            param.setExtraInfo(orderExtraObject.toJSONString());

            String orderNo = new Date().getTime() + Utility.randomString(7);
            param.setOrderNo(orderNo);

            if (mockTotalFee != null) {
                totalFee = mockTotalFee;
            } else if (accountInfo.getPhone().equals("18611699741")) {
                totalFee = 1;
            }
            ////////////////////////////////////////////////////////////////////////
            Utility.switchConfig(usesConfig1, payService.getEngine(), null);
//            Map<String, String> wxPayInfo = payService.wxPreOrder(
//                    openId, "商品描述", totalFee, orderNo, Utility.getRemoteIP(request),
//                    "http://kuaisu.chengguokj.com/ajax/wxpay/payCallback",
//                    (orderType == PreOrderType.wx || orderType == PreOrderType.app) ? "JSAPI" : "MWEB"
//            );
            Map<String, String> wxPayInfo = payService.wxPreOrder(
                    openId, "商品描述", totalFee, orderNo, Utility.getRemoteIP(request),
                    oldNotifyUrl,
                    (orderType == PreOrderType.wx || orderType == PreOrderType.app) ? "JSAPI" : "MWEB"
            );
            ////////////////////////////////////////////////////////////////////////

            PreOrderInfo preOrderInfo = new PreOrderInfo();
            //preOrderInfo.setCharge(charge.toString());
            preOrderInfo.setAccountInfo(accountInfo);
            preOrderInfo.setOrderInfo(param);
            preOrderInfo.setRoomInfo(roomInfo);
            preOrderInfo.setHotelInfo(hotelInfo);
            preOrderInfo.setDays(days);
            preOrderInfo.setWxpayInfo(wxPayInfo);
            orderService.insertPreOrder(preOrderInfo);

            responseBody.setBody(preOrderInfo);

            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(e.getMessage());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(e.getMessage());
        }

        return ResponseEntity.ok(responseBody);
    }

    /**
     * 确认订单
     *
     * @param token
     * @param orderNo
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/confirmOrder", method = RequestMethod.POST)
    public ResponseEntity<?> confirmOrder(
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "orderNo", defaultValue = "", required = true) String orderNo
    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {
            AccountInfo accountInfo = accountService.token2AccountInfo(token);
            if (accountInfo.getType() != AccountType.User) {
                throw new PermissionInvalidException();
            }

            if (orderNo == null || orderNo.isEmpty()) {
                throw new InvalidParameterException("orderNo is invalid.");
            }

            OrderInfo orderInfo = orderInfoMapper.selectByOrderNo(orderNo);
            if (orderInfo == null) {
                responseBody.addMapBody("payed", false);
                responseBody.addMapBody("orderInfo", null);
            } else {
                responseBody.addMapBody("payed", orderInfo.getStatus() == OrderStatus.preCheckIn || orderInfo.getStatus() == OrderStatus.postCheckOut);
                responseBody.addMapBody("orderInfo", orderInfo);
            }

            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(e.getMessage());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL.getMsg());
        }

        return ResponseEntity.ok(responseBody);
    }

    /**
     * 查询各状态的订单数
     *
     * @param token
     * @param pageNum
     * @param pageCount
     * @param status
     * @param type
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/list", method = RequestMethod.POST)
    public ResponseEntity<?> list(
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "pageNum", defaultValue = "0", required = true) Integer pageNum, // 页号
            @RequestParam(name = "pageCount", defaultValue = "0", required = true) Integer pageCount, // 每页条目数（条目数为房间总量）
            @RequestParam(name = "status", defaultValue = "0", required = true) Integer status,
            @RequestParam(name = "type", defaultValue = "0", required = true) Integer type
    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {
            AccountInfo accountInfo = accountService.token2AccountInfo(token);
            if (accountInfo.getType() != AccountType.User) {
                throw new PermissionInvalidException();
            }

            // 兼容前端
            List<Integer> statusList = null;
            if (type == null || type == 0 || type == 1) {
                statusList = null;
            } else if (type == 2) { // 已付款
                statusList = new ArrayList<>();
                statusList.add(OrderStatus.preCheckIn);
                statusList.add(OrderStatus.postCheckOut);
                statusList.add(OrderStatus.preRefund);
                statusList.add(OrderStatus.living);
            } else if (type == 3) {
                statusList = new ArrayList<>();
                statusList.add(OrderStatus.prePay);
            } else if (type == 4) {
                statusList = new ArrayList<>();
                statusList.add(OrderStatus.payFailed);
                statusList.add(OrderStatus.postRefund);
            }


            List<OrderInfo> orderInfos = orderInfoMapper.selectByUser(accountInfo.getId(), statusList, (pageNum - 1) * pageCount, pageCount);

            responseBody.setBody(orderInfos);
            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL.getMsg());
        }

        return ResponseEntity.ok(responseBody);
    }

    /**
     * 查询订单详情
     *
     * @param token
     * @param id
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/getInfo", method = RequestMethod.POST)
    public ResponseEntity<?> getInfo(
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "id", defaultValue = "0", required = true) Integer id // ID
    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {
            AccountInfo accountInfo = accountService.token2AccountInfo(token);
            if (accountInfo.getType() != AccountType.User) {
                throw new PermissionInvalidException();
            }
            if (id == 0) {
                throw new InvalidParameterException();
            }

            OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(id);

            responseBody.setBody(orderInfo);
            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL.getMsg());
        }

        return ResponseEntity.ok(responseBody);
    }

    /**
     * 查询订单详情V2(3.0)
     *
     * @param token
     * @param id
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/getInfoV2", method = RequestMethod.POST)
    public ResponseEntity<?> getInfoV2(
            @RequestParam(name = "token") String token,
            @RequestParam(name = "id") Integer id // ID
    ) {
        UserInfo userInfo = accountService.tokenUserInfo(token);
        OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(id);
        if (orderInfo == null) {
            throw new ServiceException("该订单不存在");
        }
        if (!userInfo.getId().equals(orderInfo.getUserId())) {
            throw new ServiceException("非本人订单");
        }
        return ResponseEntity.ok(AjaxResponseBody.success(orderInfo));
    }


//    @Autowired
//    private SimpMessagingTemplate messagingTemplate;

//    @RequestMapping(value = "/sendmsg", method = RequestMethod.POST)
//    public void sendmsg(
//            @RequestParam(name = "user", defaultValue = "", required = true) String user,
//            @RequestParam(name = "msg", defaultValue = "", required = true) String msg // ID
//    ) {
//        messagingTemplate.convertAndSend("/broker", "abcd");
//    }

    /**
     * 通过获取openid
     *
     * @param token
     * @param code
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/getWxOpenId", method = RequestMethod.POST)
    public ResponseEntity<?> getWxOpenId(
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "code", defaultValue = "", required = true) String code
    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {
            AccountInfo accountInfo = accountService.token2AccountInfo(token);

            String refreshToken = null;
            String openId = null;
            if (code == null || code.isEmpty()) {
                refreshToken = WXPayUtility.extraInfo2refreshToken(accountInfo.getExtraInfo());

                if (refreshToken == null || refreshToken.isEmpty()) {
                    responseBody.setCode(ResponseStatus.RS_WXPAY_REFRESH_TOKEN_INVALID.getCode());
                    responseBody.setMsg(ResponseStatus.RS_WXPAY_REFRESH_TOKEN_INVALID.getMsg());
                    return ResponseEntity.ok(responseBody);
                }
            } else {
                JSONObject jsonObject = WXPayUtility.code2Object(code);

                refreshToken = WXPayUtility.codeObject2refreshToken(jsonObject);
                openId = WXPayUtility.codeObject2openId(jsonObject);

                if (refreshToken == null || refreshToken.isEmpty()) {
                    responseBody.setCode(ResponseStatus.RS_WXPAY_CODE_INVALID.getCode());
                    responseBody.setMsg(ResponseStatus.RS_WXPAY_CODE_INVALID.getMsg());
                    return ResponseEntity.ok(responseBody);
                }

                if (refreshToken != null && !refreshToken.isEmpty()) {
                    String extraInfo = WXPayUtility.setRefreshTokenToExtraInfo(accountInfo.getExtraInfo(), refreshToken);
                    accountInfoMapper.updateExtraInfoById(accountInfo.getId(), extraInfo);
                }
            }

            if (openId == null || openId.isEmpty()) {
                openId = WXPayUtility.refreshToken2openId(refreshToken);
            }

            if (openId == null || openId.isEmpty()) {
                responseBody.setCode(ResponseStatus.RS_WXPAY_REFRESH_TOKEN_INVALID.getCode());
                responseBody.setMsg(ResponseStatus.RS_WXPAY_REFRESH_TOKEN_INVALID.getMsg());
                return ResponseEntity.ok(responseBody);
            }

            responseBody.setBody(openId);
            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL.getMsg());
        }

        return ResponseEntity.ok(responseBody);
    }

    /**
     * 通过获取openid(V2)
     *
     * @param token
     * @param type(小程序xcx,公众号gzh)
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/getWxOpenIdV2", method = RequestMethod.POST)
    public ResponseEntity<?> getWxOpenIdV2(
            @RequestParam(name = "token") String token,
            @RequestParam(name = "type", defaultValue = "xcx") String type) {
        UserInfo userInfo = accountService.tokenUserInfo(token);
        Map<String, String> map = new HashMap<>();
        JSONObject jsonObject = JSONObject.parseObject(userInfo.getExtraInfo());
        if (jsonObject == null) {
            map.put("openId", null);
            return ResponseEntity.ok(AjaxResponseBody.success(map));
        }
        if ("xcx".equals(type)) {
            String xcxOpenId = (String) jsonObject.get("xcxOpenId");
            if (Arguments.notEmpty(xcxOpenId)) {
                map.put("openId", xcxOpenId);
            } else {
                map.put("openId", null);
            }
        }
        if ("gzh".equals(type)) {
            String gzhOpenId = (String) jsonObject.get("gzhOpenId");
            if (Arguments.notEmpty(gzhOpenId)) {
                map.put("openId", gzhOpenId);
            } else {
                map.put("openId", null);
            }
        }
        return ResponseEntity.ok(AjaxResponseBody.success(map));
    }

    /**
     * 通过指定条件查询订单
     *
     * @param token
     * @param hotelId
     * @param status
     * @param orderNo
     * @param orderDate
     * @param checkIn
     * @param checkOut
     * @param keyword
     * @param pageNum
     * @param pageSize
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/listEx", method = RequestMethod.POST)
    public ResponseEntity<?> listEx(
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "hotelId", defaultValue = "0", required = true) Integer hotelId,
            @RequestParam(name = "status", defaultValue = "", required = true) String status,
            @RequestParam(name = "orderNo", defaultValue = "", required = true) String orderNo,
            @RequestParam(name = "orderDate", defaultValue = "", required = true) String orderDate,
            @RequestParam(name = "checkIn", defaultValue = "", required = true) String checkIn,
            @RequestParam(name = "checkOut", defaultValue = "", required = true) String checkOut,
            @RequestParam(name = "keyword", defaultValue = "", required = true) String keyword,
            @RequestParam(name = "pageNum", defaultValue = "0", required = true) Integer pageNum,
            @RequestParam(name = "pageSize", defaultValue = "0", required = true) Integer pageSize
    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {
            Integer offset = null, maxrow = null;
            if (pageNum != 0 && pageSize != 0) {
                offset = (pageNum - 1) * pageSize;
                maxrow = pageSize;
            }

            AccountInfo accountInfo = accountService.token2AccountInfo(token);

            List<Integer> hotelIds = null;
            if (accountInfo.getType() == AccountType.User) {
                throw new PermissionInvalidException();
            } else if (accountInfo.getType() == AccountType.HotelManager) {
                List<HotelInfo> hotelInfoList = accountService.searchHotelsByOperator(token);
                if (hotelInfoList == null || hotelInfoList.isEmpty()) {
                    throw new Exception("hotel information is empty");
                }

                hotelIds = new ArrayList<>();
                for (HotelInfo hotelInfo : hotelInfoList) {
                    hotelIds.add(hotelInfo.getId());
                }
            } else if (accountInfo.getType() == AccountType.CityManager) {

                List<HotelInfo> hotelInfoList = accountService.searchHotelsByAdministrator(accountInfo.getId());
                if (hotelInfoList == null || hotelInfoList.isEmpty()) {
                    throw new Exception("hotel information is empty");
                }

                hotelIds = new ArrayList<>();
                for (HotelInfo hotelInfo : hotelInfoList) {
                    hotelIds.add(hotelInfo.getId());
                }
            } else if (hotelId != 0) {
                hotelIds = new ArrayList<>();
                hotelIds.add(hotelId);
            }

            String orderDateEnd = null;
            try {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                Calendar calendar = new GregorianCalendar();
                calendar.setTime(simpleDateFormat.parse(orderDate));
                calendar.add(Calendar.DATE, 1);
                orderDateEnd = simpleDateFormat.format(calendar.getTime());
            } catch (Throwable e) {
            }

            List<Integer> statusList = null;
            if (status != null && !StringUtils.isEmpty(status)) {
                statusList = Utility.str2intList(status);
            }
            List<Map> orderInfoList = orderInfoMapper.selectFromHotelMgr(
                    hotelIds,
                    statusList == null || statusList.size() == 0 ? null : statusList,
                    orderNo, orderDate, orderDateEnd,
                    checkIn, checkOut, keyword, offset, maxrow
            );
            int count = orderInfoMapper.selectFromHotelMgrCount(
                    hotelIds,
                    statusList == null || statusList.size() == 0 ? null : statusList,
                    orderNo, orderDate, orderDateEnd,
                    checkIn, checkOut, keyword
            );

            responseBody.addMapBody("list", orderInfoList);
            responseBody.addMapBody("count", count);

            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL.getMsg());
        }

        return ResponseEntity.ok(responseBody);
    }

    /**
     * 分页获取订单
     *
     * @param token
     * @param status    订单状态 全部不传
     * @param orderNo   订单id
     * @param orderDate 下单时间 格式 "yyyy-MM-dd HH:mm:ss"
     * @param checkIn   入住时间  格式 "yyyy-MM-dd HH:mm:ss"
     * @param checkOut  离店时间  格式 "yyyy-MM-dd HH:mm:ss"
     * @param pageNum   当前页（默认1）
     * @param pageSize  页码 （默认10）
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/listExV2", method = RequestMethod.POST)
    public ResponseEntity<?> listExV2(
            @RequestParam(name = "token") String token,
            @RequestParam(name = "status", required = false) String status,
            @RequestParam(name = "orderNo", required = false) String orderNo,
            @RequestParam(name = "orderDate", required = false) String orderDate,
            @RequestParam(name = "checkIn", required = false) String checkIn,
            @RequestParam(name = "checkOut", required = false) String checkOut,
            @RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize
    ) {
        AccountInfo accountInfo = accountService.token2AccountInfoV2(token);
        Map<String, Object> map;
        if (accountInfo.getType().equals(AccountType.Administrator)) { //总公司
            map = orderService.selectOrderByPage(status, orderNo, orderDate, checkIn, checkOut, pageNum, pageSize, accountInfo.getType(), accountInfo.getId());
        } else if (accountInfo.getType().equals(AccountType.BranchManager)) { //分公司
            map = orderService.selectOrderByPage(status, orderNo, orderDate, checkIn, checkOut, pageNum, pageSize, accountInfo.getType(), accountInfo.getId());
        } else if (accountInfo.getType().equals(AccountType.CityAgent)) { //市代理
            map = orderService.selectOrderByPage(status, orderNo, orderDate, checkIn, checkOut, pageNum, pageSize, accountInfo.getType(), accountInfo.getId());
        } else if (accountInfo.getType().equals(AccountType.CityManager)) { //城市运营商
            map = orderService.selectOrderByPage(status, orderNo, orderDate, checkIn, checkOut, pageNum, pageSize, accountInfo.getType(), accountInfo.getId());
        } else if (accountInfo.getType().equals(AccountType.HotelManager)) { //酒店
            map = orderService.selectOrderByPage(status, orderNo, orderDate, checkIn, checkOut, pageNum, pageSize, accountInfo.getType(), accountInfo.getId());
        } else {
            throw new ServiceException("当前用户无权限");
        }
        return ResponseEntity.ok(AjaxResponseBody.success(map));
    }

    /**
     * 用户给酒店评分
     *
     * @param token
     * @param id
     * @param score
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/orderPushScore", method = RequestMethod.POST)
    public ResponseEntity<?> orderPushScore(
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "id", defaultValue = "", required = true) Integer id,
            @RequestParam(name = "score", defaultValue = "", required = true) Integer score

    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {

            AccountInfo accountInfo = accountService.token2AccountInfo(token);
            if (accountInfo.getType() != AccountType.User) {
                throw new PermissionInvalidException();
            }

            OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(id);
            if (orderInfo.getUserId().intValue() != accountInfo.getId().intValue()) {
                throw new PermissionInvalidException();
            }

            int rec = orderInfoMapper.updateScore(id, score);
            if (rec == 0) {
                throw new Exception("score is updated before");
            }

            Integer hotelId = orderInfoMapper.getHotelIdByOrderId(id);
            if (hotelId == null || hotelId == 0) {
                throw new Exception("Error on order id to hotel id");
            }

            rec = hotelInfoMapper.updateScore(hotelId);
            if (rec == 0) {
                throw new Exception("updated 0 row");
            }

            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL.getMsg());
        }

        return ResponseEntity.ok(responseBody);
    }


    /**
     * 绑定用户相关信息
     *
     * @param request
     * @param response
     * @param id
     * @param code
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/setWxCode", method = {RequestMethod.POST, RequestMethod.GET})
    public String setWxCode(
            HttpServletRequest request, HttpServletResponse response,
            @RequestParam(name = "id", defaultValue = "", required = true) Integer id,
            @RequestParam(name = "code", defaultValue = "", required = true) String code

    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();
        AccountInfo accountInfo = accountInfoMapper.selectByPrimaryKey(id);
        if (accountInfo != null && code != null && !StringUtils.isEmpty(code)) {
            try {
                MyConfig config = new MyConfig();
                JSONObject jsonObject = payService.wxParseCode(true, code, config);
                String refreshToken = payService.wxCodeObject2RefreshToken(jsonObject);
                String openId = payService.wxObject2OpenId(jsonObject);
                if (refreshToken != null && !refreshToken.isEmpty()) {
                    String extraInfo = WXPayUtility.setRefreshTokenToExtraInfo(accountInfo.getExtraInfo(), refreshToken);
                    extraInfo = Utility.setExtraInfo(extraInfo, "openid", openId);
                    accountInfoMapper.updateExtraInfoById(accountInfo.getId(), extraInfo);
                }
            } catch (Throwable e) {
            }
        }

        return "<h1>绑定成功</h1>";
    }

    /**
     * 生成预支付订单
     *
     * @param request
     * @param response
     * @param token
     * @param id
     * @param score
     * @param code
     * @param refreshToken
     * @return
     */
    @ApiIgnore
    @RequestMapping(value = "/trans", method = RequestMethod.POST)
    public ResponseEntity<?> trans(
            HttpServletRequest request, HttpServletResponse response,
            @RequestParam(name = "token", defaultValue = "", required = true) String token,
            @RequestParam(name = "id", defaultValue = "", required = true) Integer id,
            @RequestParam(name = "score", defaultValue = "", required = true) Integer score,
            @RequestParam(name = "code", defaultValue = "", required = true) String code,
            @RequestParam(name = "refreshToken", defaultValue = "", required = true) String refreshToken

    ) {
        AjaxResponseBody responseBody = new AjaxResponseBody();

        try {

            AccountInfo accountInfo = accountService.token2AccountInfo(token);

            int orderType = PreOrderType.wx;
            boolean usesConfig1 = orderType == PreOrderType.wx || orderType == PreOrderType.h5;

            String openId = null;
            if (refreshToken == null || StringUtils.isEmpty(refreshToken)) {
                if (code != null && !code.isEmpty()) {
                    try {
                        MyConfig config = new MyConfig();
                        Utility.switchConfig(usesConfig1, null, config);
                        JSONObject jsonObject = payService.wxParseCode(usesConfig1, code, config);
                        refreshToken = payService.wxCodeObject2RefreshToken(jsonObject);
                        openId = payService.wxObject2OpenId(jsonObject);
                        if (refreshToken != null && !refreshToken.isEmpty()) {
                            String extraInfo = WXPayUtility.setRefreshTokenToExtraInfo(accountInfo.getExtraInfo(), refreshToken);
                            extraInfo = Utility.setExtraInfo(extraInfo, "openid", openId);
                            accountInfoMapper.updateExtraInfoById(accountInfo.getId(), extraInfo);
                        }
                    } catch (Throwable e) {
                    }
                }

                // 没有拿到openId，尝试从缓存中读取
                if (openId == null || openId.isEmpty()) {
                    openId = WXPayUtility.getOpenIdFromExtraInfo(accountInfo.getExtraInfo());
                }
            } else {
                openId = WXPayUtility.refreshToken2openId(refreshToken);
            }

            if (openId == null || openId.isEmpty()) {
                throw new Exception("openid error");
            }

            //String param = PayServiceImpl.genTransParam(openId, 1, Utility.getRemoteIP(request), WXPayConstants.SignType.MD5);
            //String res = Utility.sendGet("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers", param);
            EntPayResult r = payService.wxEntPay(openId, 1, Utility.getRemoteIP(request));

            responseBody.setCode(ResponseStatus.RS_SUCCESS.getCode());
            responseBody.setMsg(ResponseStatus.RS_SUCCESS.getMsg());
        } catch (TokenInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_TOKEN.getMsg());
        } catch (PermissionInvalidException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PERMISSION.getMsg());
        } catch (InvalidParameterException e) {
            responseBody.setCode(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL_INVALID_PARAMETERS.getMsg());
        } catch (Throwable e) {
            responseBody.setCode(ResponseStatus.RS_FAIL.getCode());
            responseBody.setMsg(ResponseStatus.RS_FAIL.getMsg());
        }

        return ResponseEntity.ok(responseBody);
    }
}